#undef BLENDSTRETCH
#undef BLENDPIXELSTRETCH
#undef BLENDBORDERSTRETCH

#define BLENDSTRETCH MAKE_BLEND(BLEND_MODE, PIXEL_BITS, StretchBilinear, MMX)
#define BLENDPIXELSTRETCH MAKE_BLENDPIXELSTRETCH(BLEND_MODE, PIXEL_BITS, Bilinear, MMX)
#define BLENDBORDERSTRETCH MAKE_BLENDBORDERSTRETCH(BLEND_MODE, PIXEL_BITS, Bilinear, MMX)

#ifndef BLENDALPHA
__forceinline void BLENDPIXELSTRETCH(const PBITMAPEX lpbBitmap, LPBYTE c0, LPBYTE c1, DWORD u, DWORD v, LPXARGB result)
#else
__forceinline void BLENDPIXELSTRETCH(const PBITMAPEX lpbBitmap, LPBYTE c0, LPBYTE c1, DWORD u, DWORD v, LPXARGB result, __m64& alphaMask)
#endif
{
    __m64 mu = _m_from_int(u);
    __m64 mv = _m_from_int(v);
    __m64 mzero = _mm_setzero_si64();
    __m64 mc3 = _m_from_int(GETPIXEL(lpbBitmap, c0).argb);
    __m64 mc1 = _m_from_int(GETPIXEL(lpbBitmap, c0 + PIXEL_BYTES).argb);
    mu = _m_punpcklwd(mu, mu);
    mv = _m_punpcklwd(mv, mv);
    __m64 mc2 = _m_from_int(GETPIXEL(lpbBitmap, c1).argb);
    __m64 mc0 = _m_from_int(GETPIXEL(lpbBitmap, c1 + PIXEL_BYTES).argb);
    mu = _m_punpckldq(mu, mu);
    mc0 = _m_punpcklbw(mc0, mzero);
    mc1 = _m_punpcklbw(mc1, mzero);
    mc2 = _m_punpcklbw(mc2, mzero);
    mc3 = _m_punpcklbw(mc3, mzero);
    mc0 = _m_psubw(mc0, mc2);
    mc1 = _m_psubw(mc1, mc3);
    mc2 = _m_psllwi(mc2, 8);
    mc3 = _m_psllwi(mc3, 8);
    mc0 = _m_pmullw(mc0, mu);
    mc1 = _m_pmullw(mc1, mu);
    mv = _m_punpckldq(mv, mv);
    mc0 = _m_paddw(mc0, mc2);
    mc1 = _m_paddw(mc1, mc3);
    mc0 = _m_psrlwi(mc0, 8);
    mc1 = _m_psrlwi(mc1, 8);
    mc0 = _m_psubw(mc0, mc1);
    mc1 = _m_psllwi(mc1, 8);
    mc0 = _m_pmullw(mc0, mv);
    mc0 = _m_paddw(mc0, mc1);
    mc0 = _m_psrlwi(mc0, 8);
    mc0 = _m_packuswb(mc0, mzero);
#ifdef BLENDALPHA
    BLENDPIXEL(*(LPDWORD)result, _m_to_int(mc0), alphaMask);
#else
    BLENDPIXEL(*(LPDWORD)result, _m_to_int(mc0));
#endif
}

#ifndef BLENDALPHA
__forceinline void BLENDBORDERSTRETCH(const PBITMAPEX lpbBitmap, const int x16, const int y16, LPXARGB result)
#else
__forceinline void BLENDBORDERSTRETCH(const PBITMAPEX lpbBitmap, const int x16, const int y16, LPXARGB result, __m64& alphaMask)
#endif
{
    int x = (x16 >> 16);
    int y = (y16 >> 16);
    DWORD u16 = ((WORD)(x16));
    DWORD v16 = ((WORD)(y16));

    PIXEL pixel[4];
    int x0 = min(max(x, 0), lpbBitmap->bmWidth - 1);
    int x1 = min(max(x + 1, 0), lpbBitmap->bmWidth - 1);
    int y0 = min(max(y, 0), lpbBitmap->bmHeight - 1);
    int y1 = min(max(y + 1, 0), lpbBitmap->bmHeight - 1);
    pixel[0] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y0) + x0);
    pixel[1] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y0) + x1);
    pixel[2] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y1) + x0);
    pixel[3] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y1) + x1);

#ifndef BLENDALPHA
    BLENDPIXELSTRETCH(lpbBitmap, (PBYTE)&pixel[0], (PBYTE)&pixel[2], u16 >> 8, v16 >> 8, result);
#else
    BLENDPIXELSTRETCH(lpbBitmap, (PBYTE)&pixel[0], (PBYTE)&pixel[2], u16 >> 8, v16 >> 8, result, alphaMask);
#endif
}

#ifndef BLENDALPHA
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc)
#else
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, BYTE alpha)
#endif
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight + 1;

    const int csX = -(1 << 15) + ((int)xrIntFloat16 >> 1);
    const int csY = -(1 << 15) + ((int)yrIntFloat16 >> 1);

    int dstWidth = lpbDst->bmWidth;

    int borderY1 = -csY / (int)yrIntFloat16 + 1;
    if (borderY1 >= lpbDst->bmHeight)
        borderY1 = lpbDst->bmHeight;
    int borderX1 = -csX / (int)xrIntFloat16 + 1;
    if (borderX1 >= lpbDst->bmWidth)
        borderX1 = lpbDst->bmWidth;
    int borderY2 = (((lpbSrc->bmHeight - 2) << 16) - csY) / (int)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    int borderX2 = (((lpbSrc->bmWidth - 2) << 16) - csX) / (int)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    int srcy16 = csY;

#ifdef BLENDALPHA
    DWORD dwAlpha = (DWORD)alpha + 1;
    __m64 mAlpha = _m_from_int(dwAlpha);
    mAlpha = _m_punpcklwd(mAlpha, mAlpha);
    mAlpha = _m_punpckldq(mAlpha, mAlpha);
#endif

    for (int y = 0; y < borderY1; ++y)
    {
        int srcx16 = csX;
        for (int x = 0; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }

    for (int y = borderY1; y < borderY2; ++y)
    {
        LPXARGB pDstLine = (LPXARGB)((LPBYTE)lpbDst->bmBits + y * lpbDst->bmWidthBytes);
        int srcy16 = csY + y * yrIntFloat16;
        int srcx16 = csX;
        for (int x = 0; x < borderX1; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        DWORD v8 = (srcy16 & 0xFFFF) >> 8;
        PIXEL* pSrcLine = (PIXEL*)((PBYTE)(lpbSrc->bmBits) + lpbSrc->bmWidthBytes * (srcy16 >> 16)) ;
        for (int x = borderX1; x < borderX2; ++x)
        {
            PBYTE pColor0 = (PBYTE)(&pSrcLine[srcx16 >> 16]);
            PBYTE pColor1 = pColor0 + lpbSrc->bmWidthBytes;
#ifndef BLENDALPHA
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x]);
#else
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        for (int x = borderX2; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
    }
    pDstLine = (LPXARGB)((LPBYTE)lpbDst->bmBits + borderY2 * lpbDst->bmWidthBytes);
    srcy16 = csY + borderY2 * yrIntFloat16;
    for (int y = borderY2; y < lpbDst->bmHeight; ++y)
    {
        int srcx16 = csX;
        for (int x = 0; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    _m_empty();
    return TRUE;
}

#ifndef BLENDALPHA
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
#else
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, BYTE alpha, LPCSIZE lpszDst, LPCRECT lprcClipped)
#endif
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy + 1;

    const int csX = -(1 << 15) + ((int)xrIntFloat16 >> 1);
    const int csY = -(1 << 15) + ((int)yrIntFloat16 >> 1);

    int borderY1 = -csY / (int)yrIntFloat16 + 1;
    if (borderY1 >= lpszDst->cy)
        borderY1 = lpszDst->cy;
    int borderX1 = -csX / (int)xrIntFloat16 + 1;
    if (borderX1 >= lpszDst->cx)
        borderX1 = lpszDst->cx;
    int borderY2 = (((lpbSrc->bmHeight - 2) << 16) - csY) / (int)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    int borderX2 = (((lpbSrc->bmWidth - 2) << 16) - csX) / (int)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    DWORD dstByteWidth = lpbDst->bmWidthBytes;
    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    int srcy16 = csY + lprcClipped->top * yrIntFloat16;
    int srcxOriginal16 = csX + lprcClipped->left * xrIntFloat16;

    if (borderX1 > lprcClipped->right) borderX1 = lprcClipped->right;
    if (borderX2 > lprcClipped->right) borderX2 = lprcClipped->right;
    if (borderY1 > lprcClipped->bottom) borderY1 = lprcClipped->bottom;
    if (borderY2 > lprcClipped->bottom) borderY2 = lprcClipped->bottom;
    if (borderX1 < lprcClipped->left) borderX1 = lprcClipped->left;
    if (borderX2 < lprcClipped->left) borderX2 = lprcClipped->left;
    if (borderY1 < lprcClipped->top) borderY1 = lprcClipped->top;
    if (borderY2 < lprcClipped->top) borderY2 = lprcClipped->top;

#ifdef BLENDALPHA
    DWORD dwAlpha = (DWORD)alpha + 1;
    __m64 mAlpha = _m_from_int(dwAlpha);
    mAlpha = _m_punpcklwd(mAlpha, mAlpha);
    mAlpha = _m_punpckldq(mAlpha, mAlpha);
#endif

    for (int y = lprcClipped->top; y < borderY1; ++y)
    {
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    for (int y = borderY1; y < borderY2; ++y)
    {
        LPXARGB pDstLine = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - lprcClipped->top) * lpbDst->bmWidthBytes);
        int srcy16 = csY + y * yrIntFloat16;
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < borderX1; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        DWORD v8 = (srcy16 & 0xFFFF) >> 8;
        PIXEL* pSrcLine = (PIXEL*)((PBYTE)(lpbSrc->bmBits) + lpbSrc->bmWidthBytes * (srcy16 >> 16)) ;
        for (int x = borderX1; x < borderX2; ++x)
        {
            PBYTE pColor0 = (PBYTE)(&pSrcLine[srcx16 >> 16]);
            PBYTE pColor1 = pColor0 + lpbSrc->bmWidthBytes;
#ifndef BLENDALPHA
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x - lprcClipped->left]);
#else
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x - lprcClipped->left], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        for (int x = borderX2; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
    }
    pDstLine = (LPXARGB)((LPBYTE)lpbDst->bmBits + (borderY2 - lprcClipped->top) * lpbDst->bmWidthBytes);
    srcy16 = csY + borderY2 * yrIntFloat16;
    for (int y = borderY2; y < lprcClipped->bottom; ++y)
    {
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], mAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }
    _m_empty();
    return TRUE;
}